home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 7: Sunsite
/
Linux Cubed Series 7 - Sunsite Vol 1.iso
/
system
/
news
/
readers
/
nn-tk.001
/
nn-tk~
/
nn
/
global.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-07-30
|
21KB
|
1,128 lines
/*
* (c) Copyright 1990, Kim Fabricius Storm. All rights reserved.
*
* Global declarations and auxiliary functions.
*/
#include <signal.h>
#include <pwd.h>
#include "config.h"
#include "patchlevel.h"
#ifndef HAVE_STDLIB_H
# ifdef VOID_MALLOC
void *malloc();
void *calloc();
void *realloc();
# else
char *malloc();
char *calloc();
char *realloc();
# endif
#endif
/* global.c */
static sig_type catch_keyboard __APROTO((int n));
static sig_type catch_pipe __APROTO((int n));
static sig_type catch_suspend __APROTO((int n));
static enter_log __APROTO((int type, va_list zZap));
static void mem_error __APROTO((int t, int32 bytes));
export char *home_directory;
export char *nn_directory; /* ~/.nn */
export char *news_directory; /* /usr/spool/news */
export char *news_lib_directory; /* /usr/lib/news */
export char *lib_directory; /* /usr/local/lib/nn */
export char *master_directory; /* = lib */
export char *help_directory; /* = lib/help */
export char *bin_directory = BIN_DIRECTORY;
export char *db_directory; /* /usr/spool/nn or NEWS_DIR/.nn */
export char *db_data_directory; /* ..../DATA or undefined */
export int db_data_subdirs = 0; /* set if DATA/[0-9]/ exist */
export char *news_active; /* NLIB/active or DB/ACTIVE */
export char *pager;
export char *log_file; /* = lib/Log */
export char *log_entry_filter = NULL;
export char *temp_file;
#ifndef TMP_DIRECTORY
#define TMP_DIRECTORY "/usr/tmp"
#endif
export char *tmp_directory = TMP_DIRECTORY;
export char version_id[100];
#ifdef NNTP
export int use_nntp = 0; /* bool: t iff we use nntp */
#endif
export unsigned short user_eid;
export unsigned short user_id, group_id;
export int process_id;
export int who_am_i;
export int dont_write_console = 0;
export int mail_errors_mode = 2;
extern int errno;
struct passwd *getpwuid();
extern char *getlogin(), *getenv();
#ifdef HAVE_MULTIGROUP
#ifndef NGROUPS
#include <sys/param.h>
#endif
#ifndef GIDSET_TYPE
#define GIDSET_TYPE int
#endif
static int ngroups;
static GIDSET_TYPE gidset[NGROUPS];
static int
in_grplist(gid)
GIDSET_TYPE gid;
{
int n;
if (gid == group_id) return 1;
for (n = 0; n < ngroups; ++n)
if (gidset[n] == gid) return 1;
return 0;
}
#define group_access(gpid) in_grplist((GIDSET_TYPE)(gpid))
#else
#define group_access(gid) ((gid) == group_id)
#endif
/* signal handler interface */
export int s_hangup = 0; /* hangup signal */
export int s_keyboard = 0; /* keyboard interrupt */
export int s_pipe = 0; /* broken pipe */
export int s_redraw = 0; /* redraw signal (if job control) */
#ifdef RESIZING
export int s_resized = 0; /* screen resized */
#endif
#ifdef FAKE_INTERRUPT
#include <setjmp.h>
export jmp_buf fake_keyb_sig;
export int arm_fake_keyb_sig = 0;
export char fake_keyb_siglist[NSIG];
#endif
sig_type catch_hangup(n)
int n;
{
s_hangup = 1;
signal(n, SIG_IGN);
#ifdef FAKE_INTERRUPT
if (fake_keyb_siglist[n] && arm_fake_keyb_sig)
longjmp(fake_keyb_sig, 1);
#endif
}
static sig_type catch_keyboard(n)
int n;
{
#ifdef RESET_SIGNAL_WHEN_CAUGHT
signal(n, catch_keyboard);
#endif
#ifdef FAKE_INTERRUPT
if (fake_keyb_siglist[n] && arm_fake_keyb_sig)
longjmp(fake_keyb_sig, 1);
#endif
s_keyboard++;
}
static sig_type catch_pipe(n)
int n;
{
s_pipe++;
#ifdef RESET_SIGNAL_WHEN_CAUGHT
signal(n, catch_pipe);
#endif
#ifdef FAKE_INTERRUPT
if (fake_keyb_siglist[n] && arm_fake_keyb_sig)
longjmp(fake_keyb_sig, 1);
#endif
}
#ifdef HAVE_JOBCONTROL
static sig_type catch_suspend(n)
int n;
{
s_redraw++;
#ifdef RESET_SIGNAL_WHEN_CAUGHT
signal(n, catch_suspend);
#endif
suspend_nn();
#ifdef FAKE_INTERRUPT
if (fake_keyb_siglist[n] && arm_fake_keyb_sig) {
#ifdef RESIZING
s_resized++;
#endif
longjmp(fake_keyb_sig, 1);
}
#endif
}
#endif
int
init_global()
{
#ifndef HAVE_UNISTD_H
unsigned short getuid();
int getpid();
#endif
#ifdef FAKE_INTERRUPT
for (i = 0; i < NSIG; i++) fake_keyb_siglist[i] = i == 2 ? 1 : 0;
#endif
if (who_am_i != I_AM_NN) {
signal(SIGINT, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
}
signal(SIGTERM, catch_hangup);
signal(SIGHUP, catch_hangup);
signal(SIGPIPE, catch_pipe);
signal(SIGALRM, SIG_IGN);
#ifdef SIGPWR
signal(SIGPWR, catch_hangup);
#endif
#ifdef CONFIG_NUM_IN_VERSION
sprintf(version_id, "%s.%d%s #%d", RELEASE, PATCHLEVEL, BETA,
#include "update.h"
);
#else
sprintf(version_id, "%s.%d%s", RELEASE, PATCHLEVEL, BETA);
#endif
user_id = getuid();
#ifdef HAVE_MULTIGROUP
ngroups = getgroups(NGROUPS, gidset); /* Get users's group set */
#endif
group_id = getegid();
user_eid = geteuid();
process_id = getpid();
#ifdef CLIENT_DIRECTORY
lib_directory = CLIENT_DIRECTORY;
#else
lib_directory = LIB_DIRECTORY;
#endif
#ifdef NEWS_DIRECTORY
news_directory = NEWS_DIRECTORY;
#else
news_directory = "/usr/spool/news";
#endif
#ifdef DB_DIRECTORY
db_directory = DB_DIRECTORY;
#else
db_directory = mk_file_name(news_directory, ".nn");
#endif
#ifdef ACCOUNTING
if (who_am_i == I_AM_ACCT)
return 0;
#endif
#ifdef DB_DATA_DIRECTORY
db_data_directory = DB_DATA_DIRECTORY;
#else
#ifdef DB_DIRECTORY
db_data_directory = mk_file_name(db_directory, "DATA");
#else
db_data_directory = NULL;
#endif
#endif
#ifndef DB_LONG_NAMES
if (db_data_directory != NULL)
db_data_subdirs = file_exist(relative(db_data_directory, "0"), "dx");
#endif
#ifdef NEWS_LIB_DIRECTORY
news_lib_directory = NEWS_LIB_DIRECTORY;
#else
news_lib_directory = "/usr/lib/news";
#endif
/* this may later be changed by nntp_check */
news_active = mk_file_name(news_lib_directory, "active");
#ifdef MASTER_DIRECTORY
master_directory = MASTER_DIRECTORY;
#else
master_directory = LIB_DIRECTORY;
#endif
#ifdef HELP_DIRECTORY
help_directory = HELP_DIRECTORY;
#else
help_directory = mk_file_name(lib_directory, "help");
#endif
#ifdef LOG_FILE
log_file = LOG_FILE;
#else
log_file = mk_file_name(LIB_DIRECTORY, "Log");
#endif
if (who_am_i == I_AM_SPEW)
return 0;
signal(SIGINT, catch_keyboard);
signal(SIGQUIT, catch_keyboard);
#ifndef TK
#ifdef HAVE_JOBCONTROL
signal(SIGTSTP, catch_suspend);
#endif
#endif /*TK*/
if ((home_directory = getenv("HOME")) == NULL)
nn_exitmsg(1, "No HOME environment variable");
if ((pager = getenv("PAGER")) == NULL)
pager = DEFAULT_PAGER;
nn_directory = mk_file_name(home_directory, ".nn");
if (!file_exist(nn_directory, "drwx")) {
if (who_am_i != I_AM_NN) return -1;
if (mkdir(nn_directory, 0755) < 0)
nn_exitmsg(1, "Cannot create %s directory", nn_directory);
return 1;
}
return 0;
}
void
new_temp_file()
{
static char buf[FILENAME];
static char *temp_dir = NULL;
if (temp_dir == NULL)
if ((temp_dir = getenv("TMPDIR")) == NULL)
temp_dir = tmp_directory; /* just to make test above false */
else
tmp_directory = temp_dir;
#ifdef TK
sprintf(buf, "%s/nn_XXXXXX", tmp_directory);
#else /* TK */
sprintf(buf, "%s/nn.XXXXXX", tmp_directory);
#endif /* TK */
mktemp(buf);
temp_file = buf;
}
FILE *open_file(name, mode)
char *name;
int mode;
{
FILE *f = NULL;
int fd;
if ((mode & DONT_CREATE) && !file_exist(name, (char *)NULL))
return NULL;
switch (mode & 0x0f) {
case OPEN_READ:
f = fopen(name, "r");
break;
case OPEN_UPDATE:
/* f = fopen(name, "r+"); -- not reliable on many systems (sigh) */
if ((fd = open(name, O_WRONLY)) >= 0) {
if ((f = fdopen(fd, "w")) != NULL) return f;
close(fd);
}
/* FALL THRU */
case OPEN_CREATE:
f = fopen(name, "w");
break;
case OPEN_APPEND:
f = fopen(name, "a");
break;
case OPEN_CREATE_RW:
f = fopen(name, "w+"); /* not safe on all systems -- beware */
break;
default:
sys_error("Illegal mode: open_file(%s, 0x%x)", name, mode);
}
if (f) {
if (mode & OPEN_UNLINK) unlink(name);
return f;
}
if ((mode & MUST_EXIST) == 0) return NULL;
sys_error("Cannot open file %s, mode=0x%x, errno=%d", name, mode, errno);
return NULL;
}
FILE *open_file_search_path(name, mode)
char *name;
int mode;
{
FILE *f;
if (name == NULL) return NULL;
if (*name == '/') return open_file(name, mode);
f = NULL;
if (!use_nntp)
f = open_file(relative(news_lib_directory, name), OPEN_READ);
if (f == NULL)
f= open_file(relative(lib_directory, name), OPEN_READ);
if (f == NULL)
f = open_file(relative(db_directory, name), OPEN_READ);
return f;
}
int
fgets_multi(buf, size, f)
char *buf;
int size;
register FILE *f;
{
register char *s = buf;
register int c, n = size;
while (--n > 0) {
c = getc(f);
if (c == '\\') {
if ((c = getc(f)) == NL) continue;
*s++ = '\\';
if (--n < 0) break;
}
if (c == EOF) {
*s = NUL;
return s != buf;
}
if (c == NL) {
*s = NUL;
return 1;
}
*s++ = c;
}
buf[30] = NUL;
sys_error("Line too long \"%s...\" (max %d)", buf, size);
return 0;
}
/*
* relative -- concat directory name and file name
*/
char *relative(dir, name)
char *dir, *name;
{
static char concat_path[FILENAME];
sprintf(concat_path, "%s/%s", dir, name);
return concat_path;
}
char *mk_file_name(dir, name)
char *dir, *name;
{
char *buf;
buf = newstr(strlen(dir) + strlen(name) + 2);
sprintf(buf, "%s/%s", dir, name);
return buf;
}
char *home_relative(dir)
char *dir;
{
if (dir) {
if (*dir == '/')
return copy_str(dir);
else {
if (*dir == '~' && *++dir == '/') dir++;
return mk_file_name(home_directory, dir);
}
}
return NULL;
}
char *substchr(str, c1, c2)
char *str, c1, c2;
{
char *p;
if ((p = strchr(str, c1)) != NULL) *p = c2;
return p;
}
char *copy_str(str)
char *str;
{
char *new;
new = newstr(strlen(str) + 1);
if (new) strcpy(new, str);
return new;
}
time_t m_time(f)
FILE *f;
{
struct stat st;
if (fstat(fileno(f), &st) < 0) return 0;
return st.st_mtime;
}
time_t file_exist(name, mode)
char *name;
char *mode;
{
static struct stat statb;
int mask;
if (name != NULL && stat(name, &statb)) return 0;
if (mode == NULL) return statb.st_mtime;
if (statb.st_uid == user_eid)
mask = 0700;
else if (group_access(statb.st_gid))
mask = 0070;
else
mask = 0007;
while (*mode) {
switch (*mode++) {
case 'd':
if ((statb.st_mode & S_IFMT) == S_IFDIR) continue;
errno = ENOTDIR;
return 0;
case 'f':
if ((statb.st_mode & S_IFMT) == S_IFREG) continue;
if ((statb.st_mode & S_IFMT) == 0000000) continue;
if ((statb.st_mode & S_IFMT) == S_IFDIR) {
errno = EISDIR;
return 0;
}
break;
case 'r':
if (statb.st_mode & mask & 0444) continue;
break;
case 'w':
if (statb.st_mode & mask & 0222) continue;
break;
case 'x':
if (statb.st_mode & mask & 0111) continue;
break;
}
errno = EACCES;
return 0;
}
/* all modes are ok */
return statb.st_mtime;
}
/*
* copy_file: copy (or append) src file to dest file.
*
* Returns number of characters copied or an error code:
* -1: source file not found
* -2: cannot create destination
* -3: write error
*/
int32 copy_file(src, dest, append)
char *src, *dest;
int append;
{
register FILE *s, *d;
register int32 n = 0;
register int c;
s = open_file(src, OPEN_READ);
if (s == NULL) return -1;
d = open_file(dest, append ? OPEN_APPEND : OPEN_CREATE);
if (d == NULL) {
fclose(s);
return -2;
}
n = 0;
while ((c = getc(s)) != EOF) {
putc(c, d);
n++;
}
fclose(s);
if (fclose(d) == EOF) {
if (!append) unlink(dest);
return -3;
}
return n;
}
/*
* move_file: move old file to new file, linking if possible.
*
* The third arg determines what is acceptable if the old file cannot be
* removed after copying to the new file:
* 0: must remove old, else remove new and fail,
* 1: must remove or truncate old, else remove new and fail,
* 2: just leave old if it cannot be removed or truncated.
*
* Returns positive value for success, negative for failure:
* 0: file renamed (link)
* 1: file copied, old removed
* 2: file copied, but old file is only truncated.
* 3: file copied, but old file still exist.
* -1: source file not found
* -2: cannot create destination
* -3: write error
* -4: cannot unlink/truncate old
* -5: cannot unlink new
* -6: cannot link old to new
* -9: messy situation: old and new linked on return (cannot happen?)
*/
int
move_file(old, new, may_keep_old)
char *old, *new;
int may_keep_old;
{
int32 n;
if (file_exist(new, (char *)NULL)) {
if (file_exist((char *)NULL, "d"))
return -5;
if (unlink(new) < 0) /* careful - new may be directory ? */
switch (errno) {
case ENOENT:
break;
case EACCES:
if (file_exist((char *)NULL, "w")) goto do_copy;
default:
return -5;
}
}
if (link(old, new) < 0)
switch (errno) {
case EACCES: /* can just as well try to copy */
case EXDEV:
goto do_copy;
default:
return -6;
}
if (unlink(old) == 0)
return 0;
/* we were able to link but not unlink old */
/* remove new, and attempt a copy instead */
if (unlink(new) < 0) return -9; /* cannot happen? */
do_copy:
if ((n = copy_file(old, new, 0)) < 0) return n;
if (unlink(old) == 0) return 1;
if (may_keep_old)
if (n == 0 || nn_truncate(old, (off_t)0) == 0) return 2;
if (may_keep_old == 2) return 3;
unlink(new);
return -4;
}
int
save_old_file(name, suffix)
char *name, *suffix;
{
char buf[FILENAME];
sprintf(buf, "%s%s", name, suffix);
return move_file(name, buf, 0);
}
#ifdef HAVE_SYSLOG
#include <syslog.h>
#endif /* HAVE_SYSLOG */
static int
enter_log(type, va_tail)
char type;
va_tdcl
{
FILE *log;
char *msg1, buf[512];
if (log_entry_filter != NULL)
for (msg1 = log_entry_filter; *msg1; msg1++)
if (*msg1 == type) return 1;
msg1 = va_arg1(char *);
vsprintf(buf, msg1, va_args2toN);
/* cannot use relative: one of the args may be generated by it */
log = open_file(log_file, OPEN_APPEND);
if (log == NULL) return 0;
fprintf(log, "%c: %s (%s): %s\n", type,
date_time((time_t)0), user_name(), buf);
fclose(log);
return 1;
}
/*VARARGS*/
void sys_error(va_alist)
va_dcl
{
char buf[512];
char *fmt;
#ifndef HAVE_SYSLOG
FILE *f;
#endif
use_vararg;
start_vararg;
enter_log('E', va_args1toN);
end_vararg;
start_vararg;
fmt = va_arg1(char *);
vsprintf(buf, fmt, va_args2toN);
end_vararg;
nn_exitmsg(1, "%s", buf);
}
/*
* sys_warning: like sys_error but MASTER will return with -1
* instead of exit. Clients still terminate!
*/
/*VARARGS*/
int
sys_warning(va_alist)
va_dcl
{
char buf[512];
char *fmt;
#ifndef HAVE_SYSLOG
FILE *f;
#endif
static char *last_err = NULL;
use_vararg;
start_vararg;
fmt = va_arg1(char *);
vsprintf(buf, fmt, va_args2toN);
end_vararg;
if (last_err != NULL) {
if (strcmp(last_err, buf) == 0) return -1;
free(last_err);
}
last_err = copy_str(buf);
start_vararg;
enter_log('R', va_args1toN);
end_vararg;
nn_exitmsg(1, "%s", buf);
return -1;
}
/*VARARGS*/
int
log_entry(va_alist)
va_dcl
{
int type, rval;
use_vararg;
start_vararg;
type = va_arg1(int);
rval = enter_log(type, va_args2toN);
end_vararg;
return rval;
}
char *user_name()
{
static char *user = NULL;
struct passwd *pw;
if (user == NULL) {
user = getlogin();
if (user != NULL && *user != NUL) goto out;
pw = getpwuid((int)user_id);
if (pw != NULL && pw->pw_name[0] != NUL) {
user = copy_str(pw->pw_name);
goto out;
}
user = getenv("LOGNAME");
if (user != NULL && *user != NUL) goto out;
user = getenv("USER");
if (user != NULL && *user != NUL) goto out;
user = "?";
}
out:
return user;
}
time_t cur_time()
{
time_t t;
time(&t);
return t;
}
char *date_time(t)
time_t t;
{
char *str;
if (t == (time_t)0) t = cur_time();
str = ctime(&t);
str[16] = 0;
return str+4;
}
char *plural(n)
long n;
{
return n != 1 ? "s" : "";
}
/*
* memory management
*/
/* #define MEM_DEBUG */ /* trace memory usage */
static void
mem_error(t, bytes)
int t;
int32 bytes;
{
char buf[200];
if (t == 1) {
sprintf(buf, "Alloc failed: unsigned too short to represent %ld bytes",
(long)bytes);
} else {
sprintf(buf, "Out of memory - cannot allocate %ld bytes",
(long)bytes);
}
sys_error(buf);
}
char *mem_obj(size, nelt)
unsigned size;
int32 nelt;
{
unsigned n;
char *obj;
n = nelt;
if (n != nelt) mem_error(1, nelt);
obj = calloc(n, size);
#ifdef MEM_DEBUG
printf("CALLOC(%u,%u) => %lx\n", n, size, (long)obj);
#endif
if (obj == NULL) mem_error(2, (int32)(size * nelt));
return obj;
}
char *mem_str(nelt)
int32 nelt;
{
unsigned n;
char *obj;
n = nelt;
if (n != nelt) mem_error(1, nelt);
obj = malloc(n);
#ifdef MEM_DEBUG
printf("MALLOC(%u) => %lx\n", n, (long)obj);
#endif
if (obj == NULL) mem_error(2, nelt);
return obj;
}
char *mem_resize(obj, size, nelt)
char *obj;
unsigned size;
int32 nelt;
{
unsigned n;
char *obj1;
if (obj == NULL)
return mem_obj(size, nelt);
nelt *= size;
n = nelt;
if (n != nelt) mem_error(1, nelt);
obj1 = realloc(obj, n);
#ifdef MEM_DEBUG
printf("REALLOC(%lx, %u) => %lx\n", (long)obj, n, (long)obj1);
#endif
if (obj1 == NULL) mem_error(2, (int32)size);
return obj1;
}
void
mem_clear(obj, size, nelt)
register char *obj;
unsigned size;
register int32 nelt;
{
nelt *= size;
while (--nelt >= 0) *obj++ = NUL;
}
void
mem_free(obj)
char *obj;
{
#ifdef MEM_DEBUG
printf("FREE(%lx)\n", (long)obj);
#endif
if (obj != NULL) free(obj);
}
#ifndef HAVE_MKDIR
mkdir(path, mode)
char *path;
int mode;
{
char command[FILENAME*2 + 20];
sprintf(command, "{ mkdir %s && chmod %o %s ; } > /dev/null 2>&1",
path, mode, path);
return system(command) != 0 ? -1 : 0;
}
#endif
int
nn_truncate(path, len)
char *path;
off_t len;
{
#ifdef HAVE_TRUNCATE
/* Easy... we already have it... */
return truncate(path, len);
#else /* HAVE_TRUNCATE */
int fd;
#ifndef O_TRUNC
struct stat st;
#endif
if (len != 0)
sys_error("nn_truncate(%s,%ld): non-zero length", path, (long)len);
#ifdef O_TRUNC
fd = open(path, O_WRONLY | O_TRUNC);
#else
if (stat(path, &st) < 0) return -1;
fd = creat(path, st.st_mode & 07777);
#endif
if (fd < 0) return -1;
close(fd);
return 0;
#endif
}
/* Should really have some sort of configure file and use strdup().. */
char *
strsave(s)
char *s;
{
unsigned int l;
char *buf = NULL;
if (s) {
l = strlen(s);
buf = malloc(l + 1);
if (buf) {
if (l)
strcpy(buf, s);
else
buf[0] = '\0';
}
}
return buf;
}
char *
str3save(s1, s2, s3)
char *s1, *s2, *s3;
{
unsigned int l;
char *buf = NULL;
if (!s1) s1 = "";
if (!s2) s2 = "";
if (!s3) s3 = "";
l = strlen(s1) + strlen(s2) + strlen(s3);
buf = malloc(l + 1);
if (buf) {
strcpy(buf, s1);
strcat(buf, s2);
strcat(buf, s3);
}
return buf;
}
/*
* This quick function is a hack of a replacement for fgets, but is
* unlimited in line length. It returns a pointer to a buffer which
* just happens to be malloc()ed and is reused next time. IE: you had
* best strdup() it.
*
* This was inspired by an article I saw in alt.sources but forgot to
* save.
*
* -Peter Wemm <peter@DIALix.oz.au>
*/
static char *buf = NULL; /* buffer for line */
static unsigned int size = 0; /* size of buffer */
#define INITIAL_CHUNK 256 /* Initial malloc size */
#define GROW_CHUNK 256 /* extend with mem_resize in units if this */
/*
* Get a LONG line.
* Returns a pointer to a '\0' terminated string which will be
* overwritten on the next call.
* Returns NULL if error or eof, or if nothing could be read at all.
*
* Note: It does not react very well to '\0' characters in the byte stream
*/
char *
fgetstr(f)
FILE *f;
{
unsigned int len; /* # of chars stored into buf before '\0' */
char *s;
if (size == 0 && buf == NULL) {
size = INITIAL_CHUNK;
buf = mem_obj(size, 1);
if (buf == NULL) {
size = 0;
nn_exitmsg(50, "cant allocate initial chunk\n");
return NULL;
}
}
len = 0;
while (fgets(buf + len, size - len, f) != NULL)
{
len += strlen(len + buf);
if (len > 0 && buf[len - 1] == '\n')
break; /* the whole line has been read */
size += GROW_CHUNK;
s = mem_resize(buf, size, 1);
if (!s) { /* Malloc failure */
free(buf); /* panic... */
size = 0;
buf = NULL;
nn_exitmsg(51, "out of memory: cant mem_resize chunk\n");
return NULL;
}
buf = s;
mem_clear(buf + len, size - len, 1);
}
if (len == 0) {
return NULL; /* nothing read (eof or error) */
}
/* For when we are reading from a NNTP stream. */
if (len > 1 && buf[len - 1] == '\n')
--len;
if (len > 1 && buf[len - 1] == '\r')
--len;
buf[len] = '\0'; /* unconditionally terminate string, */
/* possibly overwriting CR and/or NL */
return buf;
}